﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace SecTool.WinUI
{
    /// <summary>
    /// Dummy membership provider for application users
    /// </summary>
    [System.Xml.Serialization.XmlRoot]
    public class Membership : IDisposable
    {
        [Serializable]
        public class User
        {
            /// <summary>
            /// The summary.
            /// </summary>
            [NonSerialized]
            private SHA512CryptoServiceProvider _hashAlg;

            /// <summary>
            /// Gets or sets the summary.
            /// </summary>
            /// <value>The summary.</value>
            private SHA512CryptoServiceProvider HashAlg
            {
                get
                {
                    _hashAlg = _hashAlg ?? new SHA512CryptoServiceProvider();
                    return _hashAlg;
                }
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="T:User"/> class.
            /// </summary>
            public User()
            {
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="T:User"/> class.
            /// </summary>
            public User(string username, string password)
            {
                this.Username = username;
                this.Password = password;
            }

            /// <summary>
            /// Gets or sets the username.
            /// </summary>
            /// <value>The username.</value>
            [System.Xml.Serialization.XmlAttribute]
            public string Username
            {
                get;
                set;
            }

            /// <summary>
            /// password hash value
            /// </summary>
            public byte[] passwordHash;

            /// <summary>
            /// Gets or sets the password hash.
            /// </summary>
            /// <value>The password hash.</value>
            public byte[] PasswordHash
            {
                get { return passwordHash; }
            }

            public string Password
            {
                set
                {
                    passwordHash = HashAlg.ComputeHash(
                        System.Text.UTF8Encoding.UTF8.GetBytes(value));
                }
            }

            public bool ValidatePassword(string password)
            {
                var hash = HashAlg.ComputeHash(
                    System.Text.UTF8Encoding.UTF8.GetBytes(password));
                return hash.ToList().SequenceEqual(passwordHash);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Membership"/> class.
        /// </summary>
        public Membership()
        {
        }

        public static Membership Instance
        {
            get
            {
                Membership instance = null;
                if (File.Exists(filename))
                {
                    instance = (Membership.DeserializeFromFile(filename));
                }
                else
                {
                    instance = new Membership();
                    instance.Users = new List<User>();
                }
                return instance;
            }
        }

        /// <summary>
        /// file for persistance
        /// </summary>
        [NonSerialized]
        const string filename = "Membership.xml";

        /// <summary>
        /// Users
        /// </summary>
        public List<User> Users;

        /// <summary>
        /// Add new user
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        public void AddUser(string username, string password)
        {
            if (UserExists(username))
            {
                throw new InvalidOperationException(
                    String.Format("User with username {0} already exists!", username));
            }
            Users.Add(new User(username, password));
        }

        public bool UserExists(string username)
        {
            return Users.FirstOrDefault(u => u.Username == username) != null;
        }

        public bool ValidateUser(string username, string password)
        {
            if (!UserExists(username))
            {
                throw new InvalidOperationException(
                    String.Format("User with username {0} does note exists!", username));
            }
            return Users.First(u => u.Username.Equals(username)).ValidatePassword(password);
        }

        /// <summary>
        /// Save users data
        /// </summary>
        public void SerializeToFile()
        {
            var serializer = new System.Xml.Serialization.XmlSerializer(this.GetType());

            using (System.IO.FileStream stream = new System.IO.FileStream(filename, System.IO.FileMode.Create, FileAccess.Write))
            {
                serializer.Serialize(stream, this);
            }
        }

        /// <summary>
        /// Load users data from file
        /// </summary>
        /// <param name="filename"></param>
        /// <returns>Membership with loaded users</returns>
        public static Membership DeserializeFromFile(string filename = Membership.filename)
        {
            var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(Membership));

            using (System.IO.FileStream stream = new System.IO.FileStream(filename, System.IO.FileMode.Open, FileAccess.Read))
            {
                return deserializer.Deserialize(stream) as Membership;
            }
        }

        public void Dispose()
        {
            this.SerializeToFile();
        }
    }
}